﻿namespace NumericalData.SolvationMethods
open System
open NumericalData
open System.Diagnostics
open NumericalData.Systems

[<AbstractClass>]
type IterationMethod = 
    class
        new() = {}
        abstract member Solve :SLAE -> double -> bool -> int -> Answer


        abstract member Iterate: double -> bool -> int -> Matrix -> Vector -> (Vector*int)
        default x.Iterate accur startVectorIsZero maxIterations bMatr bVec = 
            let rec iterationMethod (prev:Vector) (iteration)  = 
//                Console.WriteLine(sprintf "prev %s" (prev.SPrint()))
                let next = (bMatr*prev.ToMatrix()).ToVector() + bVec
//                Console.WriteLine(sprintf "next %s" (next.SPrint()))
//                Console.WriteLine(Rates.FrobeniusRate(next - prev))
                if Rates.FrobeniusRate(next - prev) <= accur || iteration > maxIterations then
                    (next,iteration)
                else
                    iterationMethod next (iteration + 1)

            if startVectorIsZero then
                iterationMethod (new Vector([| for i in 0..(bVec.Length - 1) -> 0. |])) 0
            else
                iterationMethod bVec 0

    end

[<AbstractClass>]
type TraceableIterationMethod = 
    class
        inherit IterationMethod
        new() = { inherit IterationMethod() }


        abstract member TraceSolve: Solve :SLAE -> double -> bool -> int -> DiagnosticsAnswer
        default x.TraceSolve system accur startVectorIsZero maxIterations = 
            let currentProcess = Process.GetCurrentProcess()
            let watch = new Stopwatch()
            let GCMemoryStart = GC.GetTotalMemory(true)
            let processMemoryStart = currentProcess.PrivateMemorySize64
            watch.Start()

            let ans = x.Solve system accur startVectorIsZero maxIterations

            watch.Stop()
            let processMemoryStop = currentProcess.PrivateMemorySize64
            let GCMemoryStop = GC.GetTotalMemory(true)
            let usedMemory = 
                Math.Abs(processMemoryStop - processMemoryStart) + Math.Abs(GCMemoryStop - GCMemoryStart)
            new DiagnosticsAnswer(ans,watch.Elapsed,usedMemory)

        abstract member FormResult: SLAE -> Vector -> int -> double -> Answer
        default x.FormResult (sys:SLAE) (vec:Vector) (iters:int) (accur:double)  = 
            let compareVector = (sys.InitialMatrix*vec.AsMatrixColumn()).ToVector()
            let mistake = Rates.FrobeniusRate( compareVector - sys.InitialVector )
            new Answer(vec,iters,mistake,accur)
    end
